package com.domino.client.view;

import com.domino.client.mvc.Controller;
import com.domino.client.mvc.View;
import com.domino.client.mvc.ViewUpdateCallback;
import com.domino.client.view.board.BoardGameCallback;
import com.domino.client.view.board.BoardPanel;
import com.domino.client.view.historicboard.HistoricBoard;
import com.domino.shared.Player;
import com.domino.shared.model.Board;
import com.domino.shared.model.Brick;
import com.domino.shared.model.Color;
import com.domino.shared.model.Point;
import com.domino.shared.model.SimpleBoard;
import com.domino.shared.session.SessionId;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * Client controller.
 * Creates all GUI objects.
 *
 */
public class BoardGamePanel implements BoardGameCallback, View {
	
	private static int POLL_DELAY_MILLIS = 500;
	
	private Color clientColor;
	private SessionId sessionId;
	private Player opponentPlayer;
	private Player player;
	
	private BoardPanel boardPanel;
	private Board board;
	private HistoricBoard historicBoard;
	private RoundNumberListBox roundNumberListBox;
	private SessionInfoPanel sessionInfo;
	private NotificationPanel notificationPanel;
	private Timer pollSessionTimer;
	private Button passButton;
	private Button leaveGameButton;
	private Button giveMeABotButton;
	private Controller controller;
	private ViewUpdateCallback viewCallbackHandler;
	private GameInfoLabel gameInfoLabel;
	
	enum State {ClientActionTurn, OpponentActionTurn, WaitingToJoinAGame, WaitingForOpponentToJoin, Error, GameFinished};
	State state = State.WaitingToJoinAGame;
	private int size;
	
	public BoardGamePanel(Controller controller, int size) {
		this.size = size;
		
		this.controller = controller;
		viewCallbackHandler = new BoardViewCallbackHandler(this);
	
		board = new SimpleBoard(size);
		historicBoard = new HistoricBoard(size);
		
		addRoundNumberListBox(size);
		
		addBoardPanel(size);
		
		addPassButton();
		
		addLeaveGameButton();
		
		addGameInfoLabel();
		
		addGiveMeABotButton();
		
		addAppInfo();
		
		addNotificationPanel();
		
		addReklam();
		
		init();
	}

	private void addReklam() {
		try {
			RootPanel.get("reklam").getElement().setInnerHTML("");
		} catch(Exception e) {
			// do nothing
		}
	}

	private void addNotificationPanel() {
		sessionInfo = new SessionInfoPanel();
		notificationPanel = sessionInfo.createNotificationPanel(this.passButton);
	}

	private void addAppInfo() {
		HTML label = new HTML("<br><hr><iframe src=\"http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fwww.facebook.com%2Fapps%2Fapplication.php%3Fid%3D145065125539157&amp;layout=standard&amp;show_faces=false&amp;width=450&amp;action=like&amp;colorscheme=light&amp;height=80\" scrolling=\"no\" frameborder=\"0\" style=\"border:none; overflow:hidden; width:450px; height:80px;\" allowTransparency=\"true\"></iframe><br><a href=\"privacy.html\"><font size=\"2\">Privacy Policy</font></a>");
		RootPanel.get("appInfo").add(label);
	}

	private void addGiveMeABotButton() {
		giveMeABotButton = new Button("Play against bot");
		giveMeABotButton.setVisible(false);
		giveMeABotButton.setStyleName("button-pass");
		RootPanel.get("giveMeABotButton").add(giveMeABotButton);
		giveMeABotButton.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				controller.setBotController(board.getSize());
			}
		});

	}

	private void addGameInfoLabel() {
		gameInfoLabel = new GameInfoLabel();
		RootPanel.get("gameInfoLabel").add(gameInfoLabel.getWidget());
	}

	public void init() {
		player = null; 
		notificationPanel.showWait("Joining a game");
		controller.joinGame(size, viewCallbackHandler);
	}
	private void addRoundNumberListBox(int size) {
		roundNumberListBox = new RoundNumberListBox();
		roundNumberListBox.setStyleName("gameHistory");
		roundNumberListBox.setVisibleItemCount(25);
		roundNumberListBox.setHeight(Integer.toString(size*42)+"px");
		RootPanel.get("roundNumberListBox").add(roundNumberListBox);
		roundNumberListBox.addChangeHandler(new ChangeHandler() {
			
			@Override
			public void onChange(ChangeEvent event) {
				int roundNumber = roundNumberListBox.getSelectedIndex();
				if (roundNumber==board.getRoundNumber()+1) {
					updatePanel(board);
				} else {
					updatePanel(historicBoard.setRoundNumber(roundNumber, board));
				}
			}
		});
	}

	private void addBoardPanel(int size) {
		boardPanel = new BoardPanel(this, size);
		RootPanel.get("dominoBoard").add(boardPanel);
	}

	private void addPassButton() {
		passButton = new Button("Pass");
		passButton.setStyleName("button-pass");
		RootPanel.get("passButton").add(passButton);
		passButton.setEnabled(false);
		passButton.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				putBrick(null);
			}
		});
	}
	
	private void addLeaveGameButton() {
		leaveGameButton = new Button("Leave game");
		leaveGameButton.setStyleName("button-pass");
		RootPanel panel = RootPanel.get("leaveGameButton");
		//RootPanel root = RootPanel.get();
		panel.add(leaveGameButton);
		leaveGameButton.setVisible(false);
		leaveGameButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				leaveGameButton.setVisible(false);
				controller.leaveGame(sessionId,viewCallbackHandler);
			}
			
		});
	}

	private void updatePanel(Board board) {
		passButton.setEnabled(state.equals(State.ClientActionTurn)==true);
		boardPanel.updateButtons(board, state.equals(State.ClientActionTurn)==true);
		updatePoints(board);
		roundNumberListBox.setFocus(true);
	}
	
	private void updatePoints(Board board) {
		sessionInfo.updatePoints(board.getColorCount(Color.BLACK), board.getColorCount(Color.WHITE));
	}
		
	protected boolean waitForUserInput() {
		notificationPanel.hide();
		state = State.ClientActionTurn;
		pollSessionTimer.cancel();
		updatePanel(board);
		sessionInfo.updateStatInfo(state);

		// bug detect, return true if there no available bircks
		return board.getAvailblePoints(this.clientColor).size()==0;
	}

	public void waitForOpponentResponse() {
		if (state==State.OpponentActionTurn) {
			return;
		}
		state = State.OpponentActionTurn;
		updatePanel(board);
		pollSessionTimer.schedule(POLL_DELAY_MILLIS);
		notificationPanel.showWait("Opponent's turn");
		sessionInfo.updateStatInfo(state);
	}
	
	public void waitForErrorRecovery(String message) {
		// put client in wait state and
		// wait for controller to handle the error
		sessionInfo.updateStatus(message);
		if (pollSessionTimer!=null)  {this.pollSessionTimer.cancel();} 
		state = State.Error;
		updatePanel(board);
		if (pollSessionTimer!=null)  {pollSessionTimer.schedule(POLL_DELAY_MILLIS); }
		notificationPanel.showWait("An error occurred: " +message);
		sessionInfo.updateStatInfo(state);
	}

	public void waitForFinishedResponse() {
		state = State.Error;
		updatePanel(board);
		pollSessionTimer.schedule(POLL_DELAY_MILLIS);
		sessionInfo.updateStatInfo(state);
	}
	
	public void putBrick(Point point) {
		Brick brick = new Brick(point,clientColor);
		board.putBrick(brick);
		roundNumberListBox.addRound(brick);
		waitForOpponentResponse();
		controller.putBrick(point, viewCallbackHandler);
	}
	
	private void startPollSessionThread() {
		pollSessionTimer = new Timer() {

			@Override
			public void run() {
				controller.poll(board.getBricks().size(), viewCallbackHandler);
			}
		};
		debug("Started poll thread");
	}

	@Override
	public void debug(String message) {
		// do nothing
		if (message.startsWith("SuperFulis")) {
			gameInfoLabel.showSuperFulis(message);
		}
	}

	@Override
	public void notifyColor(Color color) {
		clientColor = color;
		boardPanel.setClientColor(color);
		notifyColorPlayer();
	}

	private void notifyColorPlayer() {
		sessionInfo.updatePlayer(player, clientColor);
	}

	@Override
	public void notifyError(String message) {
		waitForErrorRecovery(message);
	}

	@Override
	public void notifyGameFinished() {
		gameInfoLabel.showGameFinishedInfo();
		setGameFinished();
	}

	@Override
	public boolean notifyMakeAmove() {
		return waitForUserInput();
	}

	@Override
	public void notifyMove(Brick move) {
		board.putBrick(move);
		roundNumberListBox.addRound(move);
	}

	@Override
	public void notifyNewSessionId(SessionId sessionId) {
		if (sessionId.equals(this.sessionId)) {
			return;
		}
		if (this.sessionId==null) {
			startPollSessionThread();
			gameInfoLabel.showInviteOpponent();
			leaveGameButton.setVisible(true);
			giveMeABotButton.setVisible(true);
		} else {
			debug("Left session " + this.sessionId.getId());
			clientColor=null;
			opponentPlayer=null;
			sessionInfo.joinNewSession(player);
		} 
		this.sessionId=sessionId;
		sessionInfo.updateSessionId(sessionId);
		debug("Joined session " + sessionId.getId());
		notificationPanel.showWait("Waiting for an opponent");
	}


	@Override
	public void notifyOpponent(Player opponent) {
		gameInfoLabel.opponentJoined();
		giveMeABotButton.setVisible(false);
		
		if (opponent.equals(opponentPlayer)) {
			return;
		}
		if (this.opponentPlayer!=null) {
			debug("Player left " + opponent.getId());
		}
		this.opponentPlayer=opponent;
		sessionInfo.updateOpponent(opponent);
		debug("Player joined " + opponent.getId());
		notificationPanel.hide();
	}
	
	@Override
	public void notifyPlayer(Player player) {
		this.player = player;
		notifyColorPlayer();
	}

	@Override
	public void notifyWaitOpponentAction() {
		if (state.equals(State.GameFinished)==false) {
			waitForFinishedResponse();
		} 
		waitForOpponentResponse();
	}
	
	@Override
	public void notifyWaitForServer() {
		pollSessionTimer.schedule(POLL_DELAY_MILLIS);
	}
	
	private void setGameFinished() {
		leaveGameButton.setVisible(false);
		giveMeABotButton.setVisible(false);
		roundNumberListBox.setFinished();
		notificationPanel.gameFinished(board.getColorCount(Color.BLACK), board.getColorCount(Color.WHITE));
		sessionInfo.updateStatus("");
		updatePanel(board);
		state = State.GameFinished;
	}
	
	protected Board getBoard() {
		return board;
	}

	protected Color getClientColor() {
		return clientColor;
	}


	@Override
	public void notifyPlayerGaveUp(Color givingUpColor) {
		leaveGameButton.setVisible(false);
		giveMeABotButton.setVisible(false);
		gameInfoLabel.showGameFinishedInfo();
		if (state.equals(State.GameFinished)) {
			return;
		}
		int colorBlack = board.getColorCount(Color.BLACK);
		int colorWhite = board.getColorCount(Color.WHITE);
		if (this.state.equals(State.WaitingForOpponentToJoin)|| 
			this.state.equals(State.WaitingToJoinAGame)) {
			notificationPanel.showLeftGame();
		} else {
			if (givingUpColor.equals(clientColor)) {
				notificationPanel.showWait("You gave up!");
				if (clientColor.equals(Color.BLACK)) {
					colorBlack = 0;
				} else {
					colorWhite = 0;
				}
			} else {
				notificationPanel.showWait("Your opponent gave up!");
				if (clientColor.equals(Color.BLACK)) {
					colorWhite = 0;
				} else {
					colorBlack = 0;
				}
			}
			//roundNumberListBox.setFinished();
			notificationPanel.gameFinished(colorBlack, colorWhite);
			board.stopGame();
			updatePanel(board);
			sessionInfo.updatePoints(colorBlack, colorWhite);
			sessionInfo.updateStatus("");
			state = State.GameFinished;
		}
		
		
	}

	

 }
